using System;
using System.Collections;
using System.Text;

using ObjectRelationalMapping.Helper;

namespace ObjectRelationalMapping.Model
{
    internal class Property<T>
    {
        #region Methods

        internal string GetDataReaderType ( )
        {
            string result = string.Empty;

            switch ( Type )
            {
                case "bool":
                    result = "Boolean";
                    break;
                case "byte":
                    result = "Byte";
                    break;
                case "decimal":
                    result = "Decimal";
                    break;
                case "int":
                    result = "Int32";
                    break;
                case "string":
                    result = "String";
                    break;
                case "long":
                    result = "Int64";
                    break;
                case "short":
                    result = "Int16";
                    break;
                default:
                    result = Type;
                    break;
            }

            return result;
        }

        internal string GetEmptyDataReaderValue ( )
        {
            string result = string.Empty;

            switch ( Type.ToLower () )
            {
                case "bool":
                    result = "false";
                    break;
                case "byte":
                case "double":
                case "decimal":
                case "int":
                case "long":
                case "short":
                case "single":
                    result = "( " + Type + " ) 0";
                    break;
                case "datetime":
                    result = "DateTime.MinValue";
                    break;
                case "string":
                    result = "string.Empty";
                    break;
                default:
                    result = "null";
                    break;
            }

            return result;
        }

        internal string GetParameterType ( )
        {
            StringBuilder result = new StringBuilder ();
            result.Append ( Type );

            if ( Type == "char"
                || Type == "nchar"
                || Type == "nvarchar"
                || Type == "varchar" )
            {
                result.Append ( "( " );
                result.Append ( Length );
                result.Append ( " )" );
            }

            return result.ToString ();
        }

        internal string GetSqlDataType ()
        {
            string result = string.Empty;

            if ( typeof ( T ) == typeof ( Metaclass ) )
            {
                switch ( Type )
                {
                    case "bool":
                        result = "SqlBoolean";
                        break;
                    case "byte":
                        result = "SqlByte";
                        break;
                    case "DateTime":
                        result = "SqlDateTime";
                        break;
                    case "Decimal":
                        result = "SqlDecimal";
                        break;
                    case "Double":
                        result = "SqlDouble";
                        break;
                    case "int":
                        result = "SqlInt32";
                        break;
                    case "short":
                        result = "SqlInt16";
                        break;
                    case "Single":
                        result = "SqlSingle";
                        break;
                    case "string":
                        result = "SqlString";
                        break;
                }
            }

            return result;
        }

        #endregion

        #region Properties

        internal bool IsNullable
        {
            get { return isNullable; }
            set { isNullable = value; }
        }

        internal bool IsPrimaryKey
        {
            get { return isPrimaryKey; }
            set { isPrimaryKey = value; }
        }

        internal int Length
        {
            get { return length; }
            set { length = value; }
        }

        internal string Name
        {
            get 
            {
                string result = name;

                if ( typeof ( T ) == typeof ( Metaclass ) )
                {
                    result = Common.RemovePrefix ( name );
                }

                return result; 
            }
            set { name = value; }
        }

        internal int Percision
        {
            get { return percision; }
            set { percision = value; }
        }

        internal virtual string Type
        {
            get
            {
                string result = type;

                if ( typeof ( T ) == typeof ( Metaclass ) )
                {
                    switch ( type )
                    {
                        case "bigint":
                            result = "long";
                            break;
                        case "bit":
                            result = "bool";
                            break;
                        case "char":
                        case "nchar":
                        case "ntext":
                        case "nvarchar":
                        case "text":
                        case "varchar":
                            result = "string";
                            break;
                        case "datetime":
                        case "smalldatetime":
                            result = "DateTime";
                            break;
                        case "float":
                            result = "Double";
                            break;
                        case "money":
                        case "smallmoney":
                            result = "Decimal";
                            break;
                        case "real":
                            result = "Single";
                            break;
                        case "smallint":
                            result = "short";
                            break;
                        case "tinyint":
                            result = "byte";
                            break;
                        case "uniqueidentifier":
                            result = "Guid";
                            break;
                        case "variant":
                            result = "Object";
                            break;
                        default:
                            break;
                    }
                }

                return result;
            }
            set { type = value; }
        }

        private bool isNullable;
        private bool isPrimaryKey;
        private int length;
        private string name;
        private int percision;
        private string type;

        #endregion
    }

    internal class PropertyCollection<T> : CollectionBase
    {
        internal int Add ( Property<T> property )
        {
            return List.Add ( property );
        }

        private PropertyCollection<T> GetColumns ( bool isPrimaryKey )
        {
            PropertyCollection<T> propertyCollection =
                new PropertyCollection<T> ();

            foreach ( Property<T> property in this.List )
            {
                if ( property.IsPrimaryKey == isPrimaryKey )
                {
                    Property<T> requestedProperty = new Property<T> ();
                    requestedProperty.IsNullable = property.IsNullable;
                    requestedProperty.IsPrimaryKey = property.IsPrimaryKey;
                    requestedProperty.Length = property.Length;
                    requestedProperty.Name = property.Name;
                    requestedProperty.Percision = property.Percision;
                    requestedProperty.Type = property.Type;
                    propertyCollection.Add ( requestedProperty );
                }
            }

            return propertyCollection;
        }

        #region Properties

        internal Property<T> this[ int index ]
        {
            get { return ( Property<T> )( List[ index ] ); }
            set { List[ index ] = value; }
        }

        internal int CountOfNonPrimaryKeyProperties
        {
            get { return this.Count - CountOfPrimaryKeyProperties; }
        }

        internal short CountOfPrimaryKeyProperties
        {
            get
            {
                short result = 0;

                foreach ( Property<T> property in this.List )
                {
                    if ( property.IsPrimaryKey )
                    {
                        ++result;
                    }
                }

                return result;
            }
        }

        internal PropertyCollection<T> NonPrimaryKeyProperties
        {
            get 
            {
                if ( nonPrimaryKeyProperties == null
                    || nonPrimaryKeyProperties.Count == 0 )
                {
                    nonPrimaryKeyProperties = GetColumns ( false );
                }

                return nonPrimaryKeyProperties; 
            }
        }

        internal PropertyCollection<T> PrimaryKeyProperties
        {
            get
            {
                if ( primaryKeyProperties == null
                    || primaryKeyProperties.Count == 0 )
                {
                    primaryKeyProperties = GetColumns ( true );
                }

                return primaryKeyProperties;
            }
        }

        private PropertyCollection<T> nonPrimaryKeyProperties;
        private PropertyCollection<T> primaryKeyProperties;

        #endregion
    }
}

/*
BigInt              =   Int64
Bit                 =   Boolean
Char                =   String
DateTime            =   DateTime
Decimal             =   Decimal
Float               =   Double
Int                 =   Int32
Money               =   Decimal
NChar               =   String
NText               =   String
NVarChar            =   String
Real                =   Single
SmallDateTime       =   DateTime
SmallInt            =   Int16
SmallMoney          =   Decimal
Text                =   String
TinyInt             =   Byte
UniqueIdentifier    =   Guid
VarChar             =   String
Variant             =   Object
 */